using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Text;

using Microsoft.TeamFoundation.VersionControl.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Client;

namespace TfsSpam.Utilities
{
    /// <summary>
    /// The VersionControlService class represents the TFS version
    /// control functionality and provides support for the major
    /// version control features needed by TFS Spam.
    /// </summary>
    public class VersionControlService : ServiceBase
    {
        #region -- Constructor
        /// <summary>
        /// Standard constructor for the class. Connects to the version
        /// control service on the specified TFS server.
        /// </summary>
        /// <param name="serverUrl">The URL of the TFS server to connect to.</param>
        public VersionControlService( Uri serverUrl )
            : base( serverUrl )
        {
            _versionControl = (VersionControlServer) Server.GetService( typeof( VersionControlServer ) );
        }
        #endregion

        #region -- Public properties
        /// <summary>
        /// The version control service on the TFS server
        /// we're connected to.
        /// </summary>
        public VersionControlServer VersionControl
        {
            get { return _versionControl; }
        } private readonly VersionControlServer _versionControl;
        #endregion

        #region -- Public methods
        /// <summary>
        /// Retrieves the specified changeset with all information
        /// included.
        /// </summary>
        /// <param name="changesetId">The id of the changeset that should be retrieved.</param>
        /// <returns>The requested changeset with complete information.</returns>
        public Changeset GetChangeset( int changesetId )
        {
            return _versionControl.GetChangeset( changesetId, true, true );
        }

        /// <summary>
        /// Downloads the version of a file associated with the specified
        /// changeset it.
        /// </summary>
        /// <param name="itemId">The id of the file item to download.</param>
        /// <param name="changesetId">The changeset version of the file item that should be downloaded.</param>
        /// <param name="targetFile">The fully qualified path of the file the file item should be saved as locally.</param>
        public void DownloadFile( int itemId, int changesetId, string targetFile )
        {
            Item item = _versionControl.GetItem( itemId, changesetId );

            if ( item == null )
            {
                throw new ItemDownloadException( itemId, changesetId );
            }
            else
            {
                DownloadFile( item, targetFile );
            }
        }

        /// <summary>
        /// Downloads the specified file item to a local file.
        /// </summary>
        /// <param name="item">The file item that should be downloaded.</param>
        /// <param name="targetFile">The fully qualified path of the file the file item should be saved as locally.</param>
        public void DownloadFile( Item item, string targetFile )
        {
            if ( item == null )
            {
                throw new ArgumentNullException( "item" );
            }
            else if ( string.IsNullOrEmpty( targetFile ) )
            {
                throw new ArgumentNullException( "targetFile" );
            }
            else
            {
                ChangesetVersionSpec itemVersion = new ChangesetVersionSpec( item.ChangesetId );

                _versionControl.DownloadFile( item.ServerItem, item.DeletionId, itemVersion, targetFile );
            }
        }

        /// <summary>
        /// Retrieves the project the specified changeset belongs to.
        /// </summary>
        /// <remarks>
        /// If the changeset contains items from multiple projects, only
        /// the project the first item in the changeset belongs to is 
        /// returned.
        /// </remarks>
        /// <param name="changeset">The changeset whose project should be retrieved.</param>
        /// <returns>The TFS project the changeset belongs to.</returns>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="changeset"/> is <c>null</c>.</exception>
        public TeamProject GetTfsProject( Changeset changeset )
        {
            if ( changeset == null )
            {
                throw new ArgumentNullException( "changeset" );
            }
            else
            {
                foreach ( Change change in changeset.Changes )
                {
                    if ( change.Item != null )
                    {
                        return GetProject( change.Item );
                    }
                }

                return null;
            }
        }

        /// <summary>
        /// Retrieves the TFS project the specified item belongs to.
        /// </summary>
        /// <param name="item">The item whose project should be retrieved.</param>
        /// <returns>The TFS project the item belongs to.</returns>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="item"/> is <c>null</c>.</exception>
        public TeamProject GetProject( Item item )
        {
            if ( item == null )
            {
                throw new ArgumentNullException( "item" );
            }
            else
            {
                return (TeamProject) _versionControl.GetTeamProjectForServerPath( item.ServerItem );
            }
        }
        #endregion
    }

    /// <summary>
    /// The ItemDownloadException is thrown if the <see cref="VersionControlService"/> class
    /// is unable to download a requested item for any reason.
    /// </summary>
    [Serializable]
    public class ItemDownloadException : VersionControlException
    {
        #region -- Constructors
        /// <summary>
        /// Standard constructor for the class, sets the id of the item
        /// and changeset specified for the download.
        /// </summary>
        /// <param name="itemId">The id of the item that couldn't be downloaded.</param>
        /// <param name="changesetId">The id of the changeset that defines the version of the item to download.</param>
        public ItemDownloadException( int itemId, int changesetId ) 
            : base( string.Format( System.Globalization.CultureInfo.InvariantCulture, "Download failed for item {0} with changeset {1}", itemId, changesetId ) )
        {
            _itemId = itemId;
            _changesetId = changesetId;
        }

        /// <summary>
        /// Default constructor for the class.
        /// </summary>
        public ItemDownloadException()
        {
        }

        /// <summary>
        /// Standard overload for the Exception class.
        /// </summary>
        /// <param name="message">The message that defines the exception.</param>
        public ItemDownloadException( string message )
            : base( message )
        {
        }

        /// <summary>
        /// Standard overload for the Exception class.
        /// </summary>
        /// <param name="message">The message that defines the exception.</param>
        /// <param name="innerException">The exception that caused this execption to be thrown.</param>
        public ItemDownloadException( string message, Exception innerException )
            : base( message, innerException )
        {
        }

        /// <summary>
        /// Standard overload for the Exception class.
        /// </summary>
        /// <param name="info">The SerializationInfo that holds the serialized object data about the exception being thrown.</param>
        /// <param name="context">The StreamingContext that contains contextual information about the source or destination.</param>
        protected ItemDownloadException( System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context )
            : base( info, context )
        {
        }
        #endregion

        #region -- Public properties
        /// <summary>
        /// The id of the item that couldn't be downloaded.
        /// </summary>
        public int ItemId
        {
            get { return _itemId; }
        }
        private int _itemId;

        /// <summary>
        /// The id of the changeset that defines the version of
        /// the item that couldn't be downloaded.
        /// </summary>
        public int ChangesetId
        {
            get { return _changesetId; }
        }
        private int _changesetId;
        #endregion
    }
}
